<?php

#	Fastfoo - CMS developer under CakePHP / http://fast-foo.com
#	Copyright (C) 2010  Antonio Irlandés García

#	This program is free software: you can redistribute it and/or modify
#	it under the terms of the GNU General Public License as published by
#	the Free Software Foundation, either version 3 of the License, or
#	(at your option) any later version.

#	This program is distributed in the hope that it will be useful,
#	but WITHOUT ANY WARRANTY; without even the implied warranty of
#	MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
#	GNU General Public License for more details.

#	You should have received a copy of the GNU General Public License
#	along with this program.  If not, see <http://www.gnu.org/licenses/>.


class MenusController extends AppController{
	
	/*****************
	** Variables Generales
	*****************/
	public $name = 'Menus';//nombre controlador
	public $uses = array('Menu','Page');//modelos empleados
	public $helpers = array('Tree');//helpers empleados
	public $layout = 'admin';//disposición de diseño por defecto

	/*****************
	** Variables Administracion
	*****************/
	public $search_form = true;//mostramos el buscador si o no
	public $cmp_search = 'Menu.name';//campo de la BD que emplearemos para la BD (filtrar en las busquedas)
	public $sub_menu = array("menus" => "index");//Subsecciones en submenu
	public $action_menu = array("list_menus" => "index","add_menu"=>"view");//Acciones del menu accion
	public $lnregpp = array(20,40,60,80,100);//Numero de registros mostrados por pagina
	public $_jsonReorderTree = array();//Array reordenado para poder ser procesado por la funcion de reordenacion del arbol

	/*****************
	** Respuestas de controlador a una accion
	*****************/
	public $result_action;//Resultado de la operaciones
	public $msg_result;//Mensaje de la operacion


////////////////////////////////////////////////////////////////////////////
//////*METODOS PARA EL PUBLICO EN GENERAL*/////////////////////////////////
//////////////////////////////////////////////////////////////////////////

	/**
	 * Obtiene un listado de los hijos de un padre
	 *
	 * @param string $name indica el nombre del registro a mostrar
	 * @return array de los hijos del padre en forma de arbol
	 */
	public function listChildByName($name){

		$menu_data = $this->Menu->findByName($name);

		return $this->_arrayToTree($this->Menu->find('all', array('conditions' => 'parent_id IS NOT NULL AND lft >= "'.$menu_data['Menu']['lft'].'" AND rght <= "'.$menu_data['Menu']['rght'].'"','order' => 'lft ASC')), $menu_data['Menu']['id']);

	}



////////////////////////////////////////////////////////////////////////////
//////*ESTOS METODOS DEBEN ESTAR LIMITADOS A LOS ADMINISTRADORES*//////////
//////////////////////////////////////////////////////////////////////////


	/**
	 * Listado de ramas padres en el panel de administración
	 *
	 * @return void
	 */
	public function admin_index(){

		//numero de registros a mostrar
		if(isset($this->params['named']['nreg']) && in_array($this->params['named']['nreg'],$this->lnregpp)){
			$i_array = array_keys($this->lnregpp,$this->params['named']['nreg']);
			$nreg = $this->lnregpp[$i_array[0]];
		}else{
			$nreg = $this->lnregpp[0];
		}
		
		//palabra a buscar
		(isset($this->params['named']['search'])) ? $search = $this->params['named']['search'] : $search = null;
		//pagina
		(isset($this->params['named']['Menu'])) ? $page = $this->params['named']['Menu'] : $page = 1;

		//Parametros que paso al paginador
		$this->paginate = array('limit' => $nreg,
								'order' => null,
								'conditions' => $this->cmp_search." LIKE '%".$search."%' AND parent_id IS NULL ",
								'recursive' => -1);

		//obtengo todos los registros y setemos
		$this->set('registers', $this->paginate('Menu'));
		
		//menu derecho
		$this->set('action_menu', $this->action_menu);

		//submenu
		$this->set('sub_menu', $this->sub_menu);

		//Mostramos el buscador
		$this->set('search_form', $this->search_form);	

		//palabra buscada
		$this->set('search', $search);

		//lista de valores para mostrar por pantalla
		$this->set('lnregpp', $this->lnregpp);

		//pagina donde me encuentro
		$this->set('page', $page);

		//Valor elegido en la lista de valores
		$this->set('nreg', $nreg);

	}


	/**
	 * Muestra registro pertinente (rama padre)
	 *
	 * @param integer $id indica el identificador del registro a mostrar
	 * @return void
	 */
	public function admin_view($id = null){

		//Seteo las secciones del menu izquierdo
		$this->set('action_menu', $this->action_menu);

		//Seteo las secciones del submenu
		$this->set('sub_menu', $this->sub_menu);

		//Mostramos el buscador
		$this->set('search_form', $this->search_form);	

		//Datos del registro
		$this->Menu->id = $id;
        $this->set('register', $this->Menu->read());
	}

	/**
	 * Vista en arbol de los hijos de un padre(menu)
	 *
	 * @param integer $id indica el identificador del registro a mostrar
	 * @return void
	 */
	public function admin_tree($id){

		if(empty($id)){

			$this->Session->setFlash('invalid_register');
			$this->redirect(array('admin' => true,'action'=>'index'), null, true);

		}else{
			//Obtiene la lista de hijos
			$menu_tree = $this->admin_listChildById($id);
			$this->set(compact('menu_tree'));

			//Menu nuevo
			$this->action_menu['tree_menu'] = "tree";

			//Listado de paginas a enlazar
			$this->set('paginas', $this->Page->find('all'));

			//Seteo las secciones del menu izquierdo
			$this->set('action_menu', $this->action_menu);

			//Seteo las secciones del submenu
			$this->set('sub_menu', $this->sub_menu);

			//Seteo el id del menu
			$this->set('menu_id', $id);
		  
			//Mostramos el buscador
			$this->set('search_form', $this->search_form);		

		}

	}



	/**
	 * Añade o actualiza los registros de la BD (padres)
	 *
	 * @return void
	 */
	public function admin_add(){

 		$this->autoRender = false;
		
		if(!empty($this->data) && $this->RequestHandler->isAjax()){

			//Created
			if(empty($this->data['Menu']['id']))
				$this->data['Menu']['created'] = date("Y-m-d H:i:s"); 

			//Modified
			if(!empty($this->data['Menu']['id']))
				$this->data['Menu']['modified'] = date("Y-m-d H:i:s"); 
		
			//Aseguro la entrada de datos con sanitize
			$this->data = Sanitize::clean($this->data,
										  array('odd_spaces' => true,
												'encode' => false,
												'dollar' => true,
												'carriage' => true,
												'unicode' => true,
												'escape' => false,
												'backslash' => true));

		
			//Validamos la entrada de datos
			if ($this->Menu->create($this->data) && $this->Menu->validates()){
				//Salvamos los datos en la BD
				if ($this->Menu->save($this->data)){


					####INICIO LOG del sistema
					if(isset($this->data['Menu']['id']) && !empty($this->data['Menu']['id'])){
						$id_reg = $this->data['Menu']['id'];
						$action_reg = "update_menu";

					}else{
						$id_reg = $this->Menu->lastReg();
						$action_reg = "add_menu";
					}

					$data_log = array ( "title" => "'".$this->data['Menu']['name']."' (".__($action_reg, true).")",
										"section" => $this->name,
										"url" => Router::url(array('admin' => true,'controller'=> strtolower($this->name), 'action'=>'view', $id_reg), true),
										"created" => date("Y-m-d H:i:s")
										);

					$this->Log->addRegister($data_log);
					####FIN LOG


					//Mensaje a lanzar
					$this->result_action = true;
					$this->msg_result = "data_saved_correctly";
			
				}else{
					$this->result_action = false;
					$this->msg_result = "error_saving";
				}

			}else{
				//Errores
				$this->result_action = false;
				$this->msg_result = array_shift($this->validateErrors($this->Menu));
			}

		}else{
			$this->result_action = false;
			$this->msg_result = "form_must_fill";
		}			

		//Obtenemos la url de redireccion y sera redirigo desde Javascript
		if(!isset($this->data['Menu']['parent_id']))
			$url_redirect = Router::url(array('admin' => true,'action'=>'index'), true)."#result_system";
		else
			$url_redirect = Router::url($this->referer()."#result_system");

		//Para cuando se redirija mediante javascript siga mostrando el mensaje
		$this->Session->setFlash(__($this->msg_result, true), 'default');

		//Devuelvo datos del mensaje e identificador (array --> json)
		echo (json_encode(array("result_action" => $this->result_action,
								"msg_result" => $this->msg_result,
								"url_redirect" => $url_redirect
								)
						)
			);
	}


	/**
	 * Elimina los registros (arboles completos) de la BD
	 *
	 * @return void
	 */
	public function admin_delete(){

		if(isset($this->params['form']['list_reg'])){

			if(count($this->params['form']['list_reg']) != 0){

				for($i = 0; $i < count($this->params['form']['list_reg']); $i++){

					if($this->params['form']['list_reg'][$i] != ""){

						####INICIO LOG del sistema
						$this->Menu->id = $this->params['form']['list_reg'][$i];
						$data_register = $this->Menu->read();

						//datos que se almacenarán en el LOG del sistema y la actividad de session del usuario
						$data_log = array ( "title" => "'".$data_register['Menu']['name']."' (".__('deleted', true).")",
											"section" => $this->name,
											"url" => null,
											"created" => date("Y-m-d H:i:s")
											);

						$this->Log->addRegister($data_log);

						####FIN LOG

						///borramos pagina
						$this->Menu->delete($this->params['form']['list_reg'][$i]);

						//datos a devolver
						$this->msg_result = "del_registers_correctly";

					}else{
						//datos a devolver
						$this->msg_result = "error_process_delete";
					}
				}

			}else{
				//datos a devolver
				$this->msg_result = "select_one_register";
			}
				
		}else{
			//datos a devolver
			$this->msg_result = "no_operations";
		}

		//Mensaje que devuelve
		$this->Session->setFlash(__($this->msg_result, true), 'default');
		//Redireccion 
		$this->redirect($this->referer()."#result_system");
	}	


	/**
	 * Añade registro (hijo) a un arbol en la BD
	 *
	 * @return void
	 */
	public function admin_addChild(){

 		$this->autoRender = false;
		
		if(!empty($this->data) && $this->RequestHandler->isAjax()){

			//Created
			if(empty($this->data['Menu']['id']))
				$this->data['Menu']['created'] = date("Y-m-d H:i:s"); 

			//Modified
			if(!empty($this->data['Menu']['id']))
				$this->data['Menu']['modified'] = date("Y-m-d H:i:s"); 
		
			//Aseguro la entrada de datos con sanitize
			$this->data = Sanitize::clean($this->data,
										  array('odd_spaces' => true,
												'encode' => false,
												'dollar' => true,
												'carriage' => true,
												'unicode' => true,
												'escape' => false,
												'backslash' => true));


			//Compruebo la opcion de link que eligio
			switch($this->params['form']['link_opt']){
			  case "pages"://Modifico el campo para incluir un enlace a la pagina
				$this->data['Menu']['url'] = $this->data['Menu']['pages'];
			  break;

			  case "plugins":
				$this->data['Menu']['url'] = $this->data['Menu']['plugins'];
			  break;

			  //No hago nada
			  default:

			}

			//Elimino variables no necesarias
			unset($this->params['form']['link_opt']);
			unset($this->data['Menu']['pages']);
			unset($this->data['Menu']['plugins']);

		
			//Validamos la entrada de datos
			if ($this->Menu->create($this->data) && $this->Menu->validates()){
				//Salvamos los datos en la BD
				if ($this->Menu->save($this->data)){

					####INICIO LOG del sistema

					$data_log = array ( "title" => "'".$this->data['Menu']['name']."' (".__('add_new_child', true).")",
										"section" => $this->name,
										"url" => Router::url(array('admin' => true,'controller'=> strtolower($this->name), 'action'=>'tree', $this->data['Menu']['parent_id']), true),
										"created" => date("Y-m-d H:i:s")
										);

					$this->Log->addRegister($data_log);
					####FIN LOG

					#Obtenemos los datos para mostrar en la vista
					$tree_menu = $this->admin_listChildById($this->data['Menu']['parent_id']);
					$this->set(compact('tree_menu'));

					#Vista a renderizar
					$this->viewPath = 'elements'.DS.'admin'.DS.'menu';
					$this->render('tree_view');

					//Mensaje a lanzar
					$this->msg_result = "data_saved_correctly";
					$this->result_action = true;
			
				}else{
					//Mensaje a lanzar
					$this->msg_result = "error_saving";
					$this->result_action = false;
				}

			}else{
				//Errores
				$this->msg_result = array_shift($this->validateErrors($this->Menu));
				$this->result_action = false;
			}

		}else{
			$this->msg_result = "form_must_fill";
			$this->result_action = false;
		}		

		//Devuelvo el resultado para mostrarlo al usuario
		$this->_showMessages(__($this->msg_result, true),'main_content');

		//menu al que pertenece el Hijo
		$this->set('menu_id',$this->data['Menu']['parent_id']);

		//Elementos de render
		//Arreglamos el problema de que salga doble, o que estos desaparezcan cuando da un problema el formulario
		if(!$this->result_action){
			$this->viewPath = 'elements'.DS.'admin'.DS.'menu';
			$this->render('tree_view');
		}
	

	}

	/**
	 * Elimina registros (hijos del arbol) de la BD
	 *
	 * @return void
	 */
	public function admin_delChild(){
		 
		 $this->autoRender = false;

		if ($this->RequestHandler->isAjax()){//Si existen peticiones via AJAX

			####INICIO LOG del sistema
			$this->Menu->id = $this->params['form']['id'];
			$data_register = $this->Menu->read();
			$this->Log->addRegister(array ( "title" => __('deleted_child', true)." --> '".$data_register['Menu']['name']."'",
											"section" => $this->name,
											"url" => Router::url(array('admin' => true,'controller'=> strtolower($this->name), 'action'=>'tree',$data_register['Menu']['parent_id']), true),
											"created" => date("Y-m-d H:i:s")
										)
									);
			####FIN LOG

			//Elimina datos
			$this->Menu->delete();

		}
	}


	/**
	 * Reordena los registros (hijos del arbol) en la BD
	 *
	 * @return void
	 */
	public function admin_reorderTree(){
		 
		$this->autoRender = false;

		if ($this->RequestHandler->isAjax()){//Si existen peticiones via AJAX

			$menu_id = $this->params['form']['menu_id'];//menu padre
			$element_id = $this->params['form']['element_id'];//elemento que se modifica
			parse_str($this->params['form']['tree_menu'],$tree_menu);//Arbol que viene de la peticion

			#Datos originales del elemento desplazado
			$this->Menu->id = $element_id;
			$element_data = $this->Menu->read();

			#Arbol Nuevo (nueva ordenación)
			$tree_new = $this->_jsonArray($menu_id,$tree_menu);
		
			#Obtengo datos de las diferencias de posicion del elemento desplazado
			$new_element_data = $this->_diffDataChildren($element_data, $tree_new);

			#Realizo las operaciones con los datos obtenidos
			if($new_element_data['new_parent_id'] == $new_element_data['old_parent_id']){

				//Cambiamos la posicion del elemento
				$delta = $new_element_data['new_position'] - $new_element_data['old_position'] - 1;
				if ($delta > 0){
					$this->Menu->movedown($new_element_data['id'], abs($delta));
				} elseif ($delta < 0) {
					$this->Menu->moveup($new_element_data['id'], abs($delta));
				}

			}else{

				//Cambio el padre
 				$this->Menu->id = $new_element_data['id'];
			    $this->Menu->saveField('parent_id', $new_element_data['new_parent_id']);
				
				//Reordeno el elemento
				if ($new_element_data['new_position'] == 0){
					$this->Menu->moveup($new_element_data['id'], true);
				} else {
					$count = $this->Menu->childcount($new_element_data['new_parent_id'], true);
					$delta = $count - $new_element_data['new_position'];
					if($delta > 0){
						$this->Menu->moveup($new_element_data['id'], $delta);
					}
				}

			}

		}else{
			$this->redirect($this->referer());
		}
	}

	/**
	 * Obtiene un listado de los hijos de un padre
	 *
	 * @param integer $id indica el identificador del registro a mostrar
	 * @return array de los hijos del padre
	 */
	public function admin_listChildById($id){

		$this->Menu->id = $id;
		$menu_data = $this->Menu->read();

		$this->set('menu_id',$id);

		return $this->Menu->find('all', array('conditions' => 'parent_id IS NOT NULL AND lft >= "'.$menu_data['Menu']['lft'].'" AND rght <= "'.$menu_data['Menu']['rght'].'"','order' => 'lft ASC'));

	}



	/**
	 * Obtengo los datos de la nueva posición del elemento dentro del arbol nuevo creado por el usuario
	 *
	 * @param array $element_data elemento que hemos desplazado en el arbol
	 * @param array $tree_new arbol reordenador por el usuario
	 * @return array $data_children datos de la nueva posicion del elemento
	 */
	private function _diffDataChildren($element_data,$tree_new){

		#id del elemento original
		$data_children['id'] = $element_data['Menu']['id'];
		#padre del elemento original
		$data_children['old_parent_id'] = $element_data['Menu']['parent_id'];
		#posicion del elemento original dentro de su rama
		//obtengo los hijos de la rama donde nos encontramos
		$childs_parent = $this->Menu->find('all', array('fields' => array('id', 'parent_id'), 'conditions' => 'parent_id = "'.$data_children['old_parent_id'].'"','order' => 'lft ASC'));
		//busco la posicion que ocupa dentro de ellos
		$i = 0;
		while($element_data['Menu']['id'] != $childs_parent[$i]['Menu']['id']){
			$i++;
		}
		$data_children['old_position'] = $i;

		#nuevo padre del elemento
		$i = 0;
		while($element_data['Menu']['id'] != $tree_new[$i]['id']){
			$i++;
		}
		$data_children['new_parent_id'] = $tree_new[$i]['parent_id'];

		#nueva posicion del elemento dentro de su rama
		$i = 0;
		$pos = 0;
		while($element_data['Menu']['id'] != $tree_new[$i]['id']){
			if($data_children['new_parent_id'] == $tree_new[$i]['parent_id'])
				$pos++;
			$i++;
		}
		$data_children['new_position'] = $pos;


		return $data_children;
	}

	/**
	 * Toma los datos que llegan del menu en JSON y los reorganiza para poder ser analizados por el controlador
	 *
	 * @param integer $parent identificador del padre de la rama que estamos tratando
	 * @param array $tree arbol o rama del arbol donde nos encontramos
	 * @return array $_jsonReorderTree array reorganizado de las posiciones nuevas del arbol
	 */
	private function _jsonArray($parent, $tree){

		foreach($tree as $indice => $branch){

			//Si no llega identificador es que el primero de todos los menus y es su propio padre
			if(!isset($branch['id'])){
				$branch['id'] = $parent;
			}

			$this->_jsonReorderTree[count($this->_jsonReorderTree)]['id'] = $branch['id'];
			$this->_jsonReorderTree[count($this->_jsonReorderTree)-1]['parent_id'] = $parent;

			$aux_parent_id = $branch['id'];

			//Elimino para que no me de problemas al pasar de una rama a otra
			unset($branch['id']);

			if(is_array($branch) && count($branch) > 0){
				$this->_jsonArray($aux_parent_id,$branch);
			}

		}
		return $this->_jsonReorderTree;
	}

	/**
	 * Convierte un array lineal en uno asociativo en forma de arbol
	 *
	 * @param array $menu hijos de un arbol (array lineal)
	 * @return array $tree array reorganizado asociativo en forma de arbol
	 */
	private function _arrayToTree($menu, $root){

		//
		// Set up indexing of the above list (in case it wasn't indexed).
		//
		$lookup = array();
		foreach($menu as $item){
			$item['Children'] = array();
			$lookup[$item['Menu']['id']] = $item;
		}

		//
		// Now build tree.
		//
		$tree = array();
		foreach($lookup as $id => $foo){
			$item = &$lookup[$id];
			if($item['Menu']['parent_id'] == $root){
				$tree[$id] = &$item;
			}elseif(isset($lookup[$item['Menu']['parent_id']])){
				$lookup[$item['Menu']['parent_id']]['Children'][$id] = &$item;
			}else{
				$tree['_orphans_'][$id] = &$item;
			}
		}

		return $tree;

	}

	/**
	 * Llama a la función en javascript encargada de mostrar el mensaje
	 *
	 * @return void
	 */
	private function _showMessages($text,$parent_div){
        echo "<script type=\"text/javascript\">createMessage('".$text."','".$parent_div."');</script>";
	}


}

?>
